﻿using System.Diagnostics.Contracts;
using System.Runtime.InteropServices;

namespace O1.Kernel.Primitives
{
    internal struct MasterCell
    {
        public const int Size = CellAddress.Size;

        public readonly CellAddress PropsAddress;

        internal MasterCell(CellAddress propsAddress)
            : this()
        {
            this.PropsAddress = propsAddress;
        }

        internal static unsafe int NextShard(byte[] buffer, int offset, out CellAddress cell)
        {
            // TODO: Make the record not explicitly aligned and read each value as Cell Address.
            // Put the property ID first, then the first offset will be 6 bytes, and can read the "root" Vertex address 
            //   as part of the iteration. This means an unsharded cell has 12 bytes in a master record.
            Contract.Requires(buffer != null);
            Contract.Requires(0 <= offset);

            if (offset + CellAddress.Size >= buffer.Length)
            {
                cell = default(CellAddress);
                return -1;
            }

            cell = CellAddress.Read(buffer, offset);
            return offset + CellAddress.Size;
        }

        internal static unsafe MasterCell Read(byte[] buffer, int offset)
        {
            Contract.Requires(buffer != null);
            Contract.Requires(0 <= offset);
            Contract.Requires(offset + Size < buffer.Length, ExceptionText.ValueBufferTooSmall);

            fixed (byte* p = &buffer[offset])
            {
                return new MasterCell(*(CellAddress*)p);
            }
        }

        internal unsafe void Write(byte[] buffer, int offset, params CellAddress[] shards)
        {
            Contract.Requires(buffer != null);
            Contract.Requires(shards != null);
            Contract.Requires(0 <= offset);
            Contract.Requires(
                offset + Size + (CellAddress.Size * shards.Length) < buffer.Length,
                ExceptionText.ValueBufferTooSmall);

            fixed (byte* p = &buffer[offset])
            {
                *((CellAddress*)p) = this.PropsAddress;
                for (int i = 0; i < shards.Length; ++i)
                {
                    *((CellAddress*)(p + ((i + 1) * CellAddress.Size))) = shards[i];
                }
            }
        }
    }
}
